/**
 * Created by Weizehua on 2017/1/24.
 */
import {Singleton} from "ts.di";
import {Wrapper as AliOSSSdk} from "ali-oss";
import {Crypto} from "node-ts-crypto-promise";
import * as Uuid from "uuid/v4";

export type Region = 'oss-cn-hangzhou'
    | 'oss-cn-shanghai'
    | 'oss-cn-qingdao'
    | 'oss-cn-beijing'
    | 'oss-cn-shenzhen'
    | 'oss-cn-hongkong'
    | 'oss-us-west-1'
    | 'oss-ap-southeast-1';

export interface AliOSSOption {
    uploaderId: string,
    uploaderSecret: string,
    adminId: string,
    adminSecret: string,
    bucket: string,
    region: Region
    [idx: string]: string
}

@Singleton()
export class AliOSS {
    // uploadSdk: any;
    adminSdk: any;
    uploaderId: string;
    uploaderSecret: string;
    bucket: string;
    region: string;

    host: string;

    constructor(options: AliOSSOption) {
        this.uploaderId = options.uploaderId;
        this.uploaderSecret = options.uploaderSecret;
        this.bucket = options.bucket;
        this.region = options.region;
        this.host = `https://${this.bucket}.${this.region}.aliyuncs.com`;
        // this.uploadSdk = new AliOSSSdk({
        //     accessKeyId: this.uploaderId,
        //     accessKeySecret: this.uploaderSecret,
        //     bucket: this.bucket,
        //     region: this.region
        // });
        this.adminSdk = new AliOSSSdk({
            accessKeyId: options.adminId,
            accessKeySecret: options.adminSecret,
            bucket: this.bucket,
            region: this.region
        })
    }

    url2path(url: string) {
        return url.substr(this.host.length + 1);
    }

    path2url(path: string) {
        return `${this.host}/${path}`;
    }

    async saveToPersist(srcPath: string): Promise<string> {
        let dest = `persist/${Uuid()}`;
        await this.move(srcPath, dest);
        return dest;
    }

    async move(srcPath: string, dstPath: string) {
        await this.adminSdk.copy(dstPath, srcPath);
        await this.adminSdk.delete(srcPath);
    }

    async clientUploadPrepare(md5: string) {
        let method = 'PUT';
        let path = this.generateTempUploadPath();
        let date = new Date(Number(new Date()) - 1000 * 60 * 10);
        let headers = {
            'Content-Type': 'application/octet-stream',
            'Content-MD5': md5,
            'x-oss-date': this.generateDateString(date),
        };

        headers['authorization'] = await this.authorization(method, path, // Method and url
            headers['Content-Type'], md5,                       // File Details
            date);                       // Date

        return {
            path: path,
            headers: headers,
        }
    }

    protected async authorization(method: string, path: string, contentType: string, contentMd5: string, date: Date) {
        let datestring = this.generateDateString(date);
        let OssHeaders =
            {
                'x-oss-date': datestring
            };

        let ossHeaderString = "";
        Object.keys(OssHeaders).sort().forEach((key) => {
            ossHeaderString += `${key}:${OssHeaders[key]}\n`;
        });

        let resourceString = `/${this.bucket}/${path}`;
        let data = `${method}\n${contentMd5}\n${contentType}\n${datestring}\n${ossHeaderString}${resourceString}`;
        let sinature = await this.signature(data);
        return `OSS ${this.uploaderId}:${sinature}`;
    }

    //noinspection JSMethodCanBeStatic
    private generateTempUploadPath(): string {
        return `temp/${Uuid()}`;
    }

    //noinspection JSMethodCanBeStatic
    private generateDateString(_date: any) {
        let date = new Date(Number(_date));
        return date.toUTCString();
    }

    private async signature(str: string): Promise<string> {
        return (await Crypto.hmac(str, this.uploaderSecret, 'sha1')).toString('base64');
    }
}

